package org.hentai.bt;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.hentai.bt.Migrate;
import org.hentai.bt.anno.field.auto.FieldAutoInteger;
import org.hentai.bt.anno.field.auto.FieldAutoLong;
import org.hentai.bt.anno.field.auto.FieldAutoShort;
import org.hentai.bt.anno.field.base.FieldForeignKey;
import org.hentai.bt.anno.field.base.FieldPrimaryKey;
import org.hentai.bt.util.field.FieldAnnotationEntryFactory;
import org.hentai.bt.util.field.FieldAnnotationValue;
import org.hentai.bt.util.sql.SqlDataType;
import org.hentai.bt.util.sql.SqlDataTypeUtil;

import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 生成迁移文件：xml文件类型
 */
public class MakeMigrations {

    private Set<Class<?>> tableBeans;

    public MakeMigrations(Set<Class<?>> tableBeans) {
        this.tableBeans = tableBeans;
    }

    public void setTableBeans(Set<Class<?>> tableBeans) {
        this.tableBeans = tableBeans;
    }

    public void makeMigrations() throws IOException, DocumentException {
        // 创建 doc
        Document doc = DocumentHelper.createDocument();

        // 创建 root
        Element root = doc.addElement("tables");

        for (Class<?> table : tableBeans) {
            Element tableNode = root.addElement("table");
            tableNode.addAttribute("table", table.getSimpleName());
            makeMigration(tableNode, table);
        }

        int fileNum = 0;

        File bf = null;
        File file = new File(String.format("%04d.xml", fileNum++));
        while (file.exists()) {
            bf = file;
            file = new File(String.format("%04d.xml", fileNum++));
        }

        if (bf != null) {
            boolean changed = Migrate.xmlHasChanged(bf, doc);
            if (!changed) {
                return;
            }
        }

        XMLWriter writer = new XMLWriter(
                new FileOutputStream(file),
                new OutputFormat("\t", true)
        );

        writer.write(doc);
        writer.close();
    }

    private List<String> primary = new ArrayList<>();
    private List<String> foreign = new ArrayList<>();
    private String tableName = "";

    private void makeMigration(Element tableNode, Class<?> table) {
        tableName = tableNode.attributeValue("table");
        primary.clear();
        Field[] fields = table.getDeclaredFields();

        for (Field field : fields) {
            Element element = tableNode.addElement("field");
            element.addAttribute("name", field.getName());
            fieldSqlElement(field, element);
        }

        String primaries = "(";
        for (int i = 0; i < primary.size(); i++) {
            primaries = primaries.concat(primary.get(i));
            if (i < primary.size() - 1) {
                primaries = primaries.concat(", ");
            }
        }
        primaries = primaries.concat(")");
        tableNode.addElement("primaryKey").setText(primaries);

        String foreigns = "";
        for (int i = 0; i < foreign.size(); i++) {
            foreigns = foreigns.concat(foreign.get(i));
            if (i < foreign.size() - 1) {
                foreigns = primaries.concat(", ");
            }
        }
        tableNode.addElement("foreignKey").setText(foreigns);
    }


    /**
     * 生成表结构对应的xml文件
     * 这里叫做迁移文件
     */
    private void fieldSqlElement(Field field, Element fieldElement) {
        Element element = fieldElement.addElement("element");   // sql 数据类型
        Element isNull = fieldElement.addElement("isNull");     // 是否为空
        Element remarks = fieldElement.addElement("remarks");     // 其他

        SqlDataType datatype = SqlDataTypeUtil.getDatatype(field.getAnnotations(), null);
        element.addAttribute("name", datatype.name());

        // 一些特殊主键,直接返回
        if (field.isAnnotationPresent(FieldAutoShort.class)) {
            primary.add(field.getName());
            isNull.addAttribute("if", "false");
            remarks.addElement("remark").addAttribute("auto", "AUTO_INCREMENT");
            return;
        }
        if (field.isAnnotationPresent(FieldAutoInteger.class)) {
            primary.add(field.getName());
            isNull.addAttribute("if", "false");
            remarks.addElement("remark").addAttribute("auto", "AUTO_INCREMENT");
            return;
        }
        if (field.isAnnotationPresent(FieldAutoLong.class)) {
            primary.add(field.getName());
            isNull.addAttribute("if", "false");
            remarks.addElement("remark").addAttribute("auto", "AUTO_INCREMENT");
            return;
        }

        // 主键/主属性
        if (field.isAnnotationPresent(FieldPrimaryKey.class)) {
            primary.add(field.getName());
        }

        // 外键
        if (field.isAnnotationPresent(FieldForeignKey.class)) {
            FieldForeignKey foreignKey = field.getAnnotation(FieldForeignKey.class);

            StringBuilder foreignSql = new StringBuilder();
            foreignSql.append("FOREIGN KEY ").append(tableName).append("(")
                    .append(field.getName())
                    .append(") REFERENCES ")
                    .append(foreignKey.contactTable())
                    .append("(").append(foreignKey.contactKey()).append(")");

            // 表数据删除操作
            if (foreignKey.onDelete() != null && !foreignKey.onDelete().equals("")) {
                foreignSql.append("ON DELETE ").append(foreignKey.onDelete());
            }

            // 表数据更新操作
            if (foreignKey.onUpdate() != null && !foreignKey.onUpdate().equals("")) {
                foreignSql.append("ON UPDATE ").append(foreignKey.onDelete());
            }

            foreign.add(foreignSql.toString());
        }

        // 普通元素
        FieldAnnotationEntryFactory.FieldEntry entry = new FieldAnnotationValue(field.getAnnotations()).getAnnotationValues();
        isNull.addAttribute("if", entry.isNull());
        Map<String, String> others = entry.getOthers();

        if (others != null) {
            for (String key : others.keySet()) {
                String value = others.get(key);
                remarks.addElement("remark").addAttribute(key, value);
            }
        }
    }
}
